#pragma once

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <semaphore.h>

const size_t num = 5;

template<class T>
class loop_queue
{
public:
    loop_queue(int size = num) : _cap(size), _q(size)
    {
        pthread_mutex_init(&_p_mtx, nullptr);
        pthread_mutex_init(&_c_mtx, nullptr);
        sem_init(&_blank_sem, 0, size);
        sem_init(&_data_sem, 0, 0);
    }

    ~loop_queue()
    {
        pthread_mutex_destroy(&_p_mtx);
        pthread_mutex_destroy(&_c_mtx);
        sem_destroy(&_blank_sem);
        sem_destroy(&_data_sem);
    }

    void push(const T& data)
    {
        sem_wait(&_blank_sem);
        pthread_mutex_lock(&_p_mtx);

        _q[_p_step++] = data;
        _p_step %= _cap;

        pthread_mutex_unlock(&_p_mtx);
        sem_post(&_data_sem);
    }

    void pop(T* data)
    {
        sem_wait(&_data_sem);
        pthread_mutex_lock(&_c_mtx);

        *data = _q[_c_step++];
        _c_step %= _cap;

        pthread_mutex_unlock(&_c_mtx);
        sem_post(&_blank_sem);
    }

private:
    std::vector<T> _q;
    size_t _cap;

    size_t _p_step;
    size_t _c_step;

    pthread_mutex_t _p_mtx;
    pthread_mutex_t _c_mtx;
    sem_t _blank_sem;
    sem_t _data_sem;
};

